Skip to content

feat(service-automation)!: two packages of two namespaces may each declare a connector of the same name (ADR-0150 child B, the narrowing) - #33431

Queued
objectstack-fleet[bot] wants to merge 3 commits into
mainfrom
claude/project-thread-y6vjs1
Queued

objectstack-fleet[bot] wants to merge 3 commits into
mainfrom
claude/project-thread-y6vjs1

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

domain:server#7 · os-tesla · session_01L9qibzKCovWpJoMZD6U3NT

Requested by Tesla · project thread
Fixes #31699

Clause-②: yes (narrowing)
Metadata-protocol: added

维护者速读

  • 改了什么:连接器(objectql: narrow the phase-1 one-holder refusal from environment to namespace for webhook, connector and email_template (ADR-0150 child B, webhook family) #31699 的最后一个类型)把「一个名字一个包」收窄到「一个命名空间一个」。两个不同命名空间的包可以各自声明同名连接器,各自物化、连接、派发。
  • 怎么做到的:connector 加入 core / objectql / metadata 三个命名空间类型集合;协调器拆掉一持有者兜底,物化器的各个表和 OAuth 授权句柄按 (包, 名) 建键;引擎对每个调用方(包括无包的调用方)都问解析器,解析到的持有者没有实例时回答 none,只有插件代码注册的连接器例外;拉取按映射所在的包解析连接器;带命名空间地址的连接门(/automation/connectors/:ns/:name/connection)连接该命名空间那个包的实例。
  • 仍然拒绝:同一命名空间的共有者、没有命名空间的包与他包同名、一个包内重名、两个持有者下没有自己包戳的条目(点名两个持有者,谁都不物化)。没有包作用域注册表的组合仍然一个名字一个实例。
  • 答复变化:changeset 的 Clause-② 列了十条 FROM → TO,标为 BREAKING。单持有者部署只变三处:3b 的回退分支(解析到的包没有实例时不再用名字的唯一实例,插件代码注册的除外)、只有停用包持有的名字(停用包以外的调用方不再派发;停用包自己的流程照旧读自己的实例)、无包戳条目换持有者后到下次协调之间不派发。凭据列表、重载合并、惰性描述符审计在单持有者下都与 main 相同。
  • 与交接清单的偏差:交接 6871869590 写的是裸连接门和 GET /connectors 加 ?package=。本 PR 用已有的命名空间地址代替第二种写法,GET /connectors 不加过滤,每个描述符带 packageId。认领修订 6882996364 事先说明了这一点。
  • 先看:packages/services/service-automation/src/engine.ts(resolveConnectorInstanceFor)、src/plugin.ts(协调器的期望集合与 judgedByPackage、connectionDoorSubject)、src/connector-pull.ts(第 2 步)、packages/runtime/src/domains/automation.ts(准入块)。

What changes

  • Type sets. connector joins NAMESPACE_SCOPED_BARE_NAME_TYPES (core), PACKAGE_SCOPED_READ_TYPES (objectql) and PACKAGE_SLOTTED_STORE_TYPES (metadata). Every covered type has now narrowed.
  • Reconcile (plugin.ts):
    • The backstop is gone (refusedDeclaredConnectorNames, and refusedConnectorNames in connector-package-scope.ts).
    • Each entry is the instance of its own package. An entry with no package of its own is the instance of the package its name resolves to over every holder. Under two holders it is refused naming both and materialized as neither (connectorInstanceRefusal).
    • desired, the materialized, degraded and credential-refusal maps, retry, dematerialize and grantHandles are keyed by instance (connectorInstanceKey: name, or PACKAGE/NAME).
    • A name repeated within one package is a duplicate naming the package. A composition with no package-scoped registry keeps one instance per name (judgedByPackage, red line 1).
    • A reloaded artifact (finding: dev metadata reload does not re-ingest connector items — reconcileDeclaredConnectors reads a registry the artifact reload never refreshes for connectors #7742) supersedes the registry's entries by (package, name): an unstamped artifact item is stamped with its manifest id at capture, so a reload of one package leaves the other package's instance of the name live. With no manifest id, or no package-scoped registry, it supersedes by name, as before.
    • An OAuth2 grant handle reads its instance's fixed package. The inert-descriptor audit judges each entry by the instance a caller of its package would be served. Instance log lines name the package.
  • Dispatch (engine.ts, resolveConnectorInstanceFor):
    • Every caller asks the resolver over the served holders, a caller with no package included, so a two-holder name is refused naming both rather than answered none.
    • A resolved holder with no instance answers none, unless the name has a connector a plugin registers in code (package null, origin plugin), which is served as before. That keeps a package's catalog descriptor plus a plugin's connector working.
    • A name only disabled packages hold answers the same way (notes 6816295828 and 6820178514).
    • getConnectorDescriptors sets packageId.
  • Pull (connector-pull.ts): the mapping's connector is resolved from the mapping's holder package through the registry's optional resolveConnectorInstanceFor, and its origin, degraded state, provider (materializedProviderOf), action and inputSchema are read from that instance. New reason connector_name_refused (409).
  • Connection door: ConnectorConnectionRequest.package (spec, optional). The runtime admits a namespace address of a two-holder connector name to the addressed holder (admitAutomationAddress answers a slot, as for a flow) and passes the package. The bare door keeps its 409.
  • Credential listing: each declaring entry is matched to its own instance's state, by the instance's package (an environment row or a compatibility entry is its holder's instance); the reported packageId is the entry's stamp, as on main.
  • feat(service-automation): the connector connection store, the connect door and the OAuth callback read and write one instance by its package and name #33218 review N6: judged, not restamped. A row a moved declaration left stamped with its old package is that package's own again once it declares the name beside the new holder. Pinned.
  • Docs: content/docs/automation/connectors.mdx gains "Two packages, one connector name" and two failure-mode rows. The objectql, core and metadata module docs say the connector narrowed.

Verification

  • New pins:
    • connector-connection-door.test.ts (7): both instances materialize; the bare door 409 and the addressed begin, read and revoke per package, each with its own row and token; revoke one, the other keeps serving; an unowned entry under two holders refused; a duplicate within a package; the no-registry CONTROL; N6.
    • flow-package-scoped-resolution.test.ts: the narrowing none pin (FROM the fallback arm); the plugin catalog CONTROL; the disabled holder in both registration orders (A's caller never served B's or A's instance; a caller with no package served B; CONTROL both enabled) and a lone disabled holder; the null caller refused naming both; the no-registry CONTROL.
    • connector-pull.test.ts (4) and connector-pull-service-door.test.ts (1): each package's mapping pulls its own instance; the provider read per instance; holder with no instance is connector_not_registered; an environment mapping over two holders is 409 connector_name_refused.
    • connector-materialization.test.ts: two holders of different namespaces both materialize (FROM the boot refused); a reload of one holder's artifact leaves the other's instance live; the credential listing reports each of two instances' states, and a sys_metadata or __runtime__ entry over a one-holder name reads the holder's state.
    • connector-connection-door.test.ts (review round): a disabled holder at the bare door and at its own namespace address (404, nothing written), both orders; a revoked instance is held degraded; the unstamped entry's handle reads its fixed package.
    • flow-package-scoped-resolution.test.ts (review round): a caller inside the disabled package reads its own instance (both orders, and alone); two catalog holders beside a plugin connector (each package served the plugin's, the null caller refused naming both).
    • connector-pull.test.ts (review round): one of two holders disabled, the environment's mapping pulls through the served holder.
    • connector-descriptor-audit.test.ts: a package's catalog entry is not silenced by another package's live instance of its name.
    • runtime automation-addressed-route.test.ts: each namespace address reads its own package's instance (FROM 409).
    • marketplace-install-local-bare-name-conflict.test.ts: the refusal case is now a same-namespace co-owner; a cross-namespace connector install is admitted (FROM 422).
  • Updated pins: the one-holder type-set pins in core, objectql and metadata; the CLI stored-references report (no sealed clash left).
  • Ablations A1 to A14 (scripts/ablation-replace.mjs, each restored with an empty git diff HEAD), each red on its pins: the fallback arm back (3), the disabled branch off (1), the null caller not asking the resolver (8), no plugin instance (1), the unowned entry not refused (1), no registry judged (6), grant handles by name (5), the pull by bare name (4), the door ignoring the package (3), the provider by name (1), no connector slot at the runtime address (1); the reload merge by bare name (1, A12); the credential listing by stamp (1, A13); the inert audit by name (1, A14). Record: server-seat-7/r2026-11-14/31699-narrow-ablations.txt.
  • Full suites before the rebase: service-automation 3923, core 3480, objectql 12967, metadata 2479, metadata-protocol 22848 (43 skipped), cloud-connection 1207. After the rebase on df9fe946: service-automation 3941, runtime 12060 (19 skipped), core 3480, objectql 12976, metadata 2479, metadata-protocol 22848 (43 skipped), cloud-connection 1210, CLI stored-references 13. All passed. Rebased again on 4ace1fa9 over fix(metadata-protocol)!: history and rollback doors refuse a bare two-holder name and address one holder by ?package= #33193 (head 1530146d): metadata 2490, metadata-protocol 22865 (43 skipped), service-automation 3941, runtime 12074 (19 skipped), spec 28422 (1 todo), spec check:generated green.
  • Gates: tsc --noEmit (service-automation, runtime), ESLint on every changed file, tenant-audit, system-context, route-ledger and engine-double censuses, changeset no-major / empty / fixed, doc-authoring, doc-anchors, spec check:generated (its test-typecheck leg needs a larger Node heap here: NODE_OPTIONS=--max-old-space-size=12288; then OK, no debt moved). dispatch-gates --ran: 124 derived families accounted for, 122 run green, 2 NOT-MEASURED for prerequisites this checkout lacks and the diff does not touch: check:dual-build-cjs-loads (no studio / embedder-openai dist) and the check-plugin-teardown-shape self-test (shallow clone). check:dts-closure was red once in the sweep and green when re-run alone (311/311 declaration files present).

Contract review

An isolated contract reviewer read the card, ADR-0150, the handoff and the diff with no anchor on mine.

  • r1 on 4a9d32b9: PASS-WITH-CHANGES. Two must-fix items: M1, the credential listing lost an environment row's state; M2, a reload of one package's artifact tore down the other package's instance. Five should-fix items (S1 to S5) and four nits.
  • Every item was taken: fixed in code (M1, M2, the nits), or declared in the changeset and docs with FROM and TO (S1 to S4), with pins for S5. The ablations were extended to A12 to A14.
  • delta-1 on de90aedb: PASS. Three notes; I acted on all three: no-registry reloads supersede by name as on main, a changeset clause, and a __runtime__ pin.
  • delta-2 on 510a8400: PASS.
  • delta-3 on e467615b, the rebase over fix(metadata-protocol)!: history and rollback doors refuse a bare two-holder name and address one holder by ?package= #33193: PASS. connector joins PACKAGE_SLOTTED_METADATA_TYPES (@objectstack/spec), the one list the history and rollback doors and the metadata store read; the changeset says what moves on those doors.

The brief, the r1 record and the delta record are posted as comments on this PR.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (Two optional members on TypeScript interfaces; no schema.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes on the changed files.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: check:generated is green for its artifacts.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L9qibzKCovWpJoMZD6U3NT

…clare a connector of the same name, and a flow, a pull and the connection door address one by package

The connector, the last type of ADR-0150 child B, narrows its one-holder
rule to the namespace: it joins the namespace-scoped type sets (core,
objectql, metadata), the declared-connector reconcile materializes each
package's instance under its package (the backstop is lifted), the engine
resolves a bare name for every caller over the served holders, a pull
resolves its connector from the mapping's package, and the namespace
connection door connects the addressed package's instance.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzKCovWpJoMZD6U3NT
@os-tesla
os-tesla force-pushed the claude/project-thread-y6vjs1 branch from 510a840 to 1530146 Compare November 14, 2026 21:22
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 14, 2026
@github-actions

github-actions Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 7 package(s): @objectstack/core, @objectstack/metadata-protocol, @objectstack/metadata, @objectstack/objectql, @objectstack/runtime, @objectstack/service-automation, @objectstack/spec, touching 68 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/metadata/src/package-slotted-store.ts, packages/objectql/src/bare-name-one-holder.ts, packages/services/service-automation/src/connector-instances.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

33 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json f068cde84d272d25c451003996f3752bd016001b.

⛔ 15 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/metadata/src/package-slotted-store.ts, packages/objectql/src/bare-name-one-holder.ts, packages/services/service-automation/src/connector-instances.ts, …) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 65 of 247 client-bound route-ledger rows — the other 182 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 182: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 123 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 186 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f068cde84d272d25c451003996f3752bd016001b → packageMentionDocs.

Which tree this was computed on

This run read content/docs from f797f5d07ebadc911000ee311aa3f80bcdab8ff9 — the merge of head 35a988b6bba31b9973c95fb5c2f0798fc213f3ec into base f068cde84d272d25c451003996f3752bd016001b, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f797f5d07ebadc911000ee311aa3f80bcdab8ff9 && git checkout f797f5d07ebadc911000ee311aa3f80bcdab8ff9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f068cde84d272d25c451003996f3752bd016001b 35a988b6bba31b9973c95fb5c2f0798fc213f3ec && git checkout -B drift-repro f068cde84d272d25c451003996f3752bd016001b && git merge --no-ff 35a988b6bba31b9973c95fb5c2f0798fc213f3ec

node scripts/docs-audit/affected-docs.mjs --json f068cde84d272d25c451003996f3752bd016001b

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs f068cde84d272d25c451003996f3752bd016001b → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzKCovWpJoMZD6U3NT

Contract review brief — #31699 narrowing PR (connector, last PR of ADR-0150 child B)

Reviewer: an isolated agent with no part in writing the change. Read the code, not the author's summary, and judge it yourself.

What to read

  • The change: git -C /home/user/objectstack-issue-31699 show 4a9d32b9 (one commit on origin/main df9fe94). Read every hunk, and the surrounding code where a hunk's correctness depends on it.
  • The changeset in that commit: .changeset/31699-connector-names-per-namespace.md. Its Clause-② line must list every answer that moves for a caller that worked before, each with FROM and TO.
  • The PR body draft: /mnt/project-files/server-seat-7/r2026-11-14/31699-narrow-body.md.
  • The card's requirements: GitHub issue objectql: narrow the phase-1 one-holder refusal from environment to namespace for webhook, connector and email_template (ADR-0150 child B, webhook family) #31699, comments 6871869590 (handover, "The narrowing PR" list), 6879929803 (addendum), 6816295828 and 6820178514 (a disabled holder at the connector dispatch door: "resolve and count over one holder set, the served set"), 6882996364 (this PR's file surface and its one stated deviation).
  • ADR-0150 (docs/adr/0150-*.md in the worktree), especially D2, D3, D4, D9, D10 and the epic red lines:
    1. Resolution never loosens: no caller is served an item it could not reach before, and no other package's body is served or sent.
    2. Ambiguity is reported, never guessed: two candidates are refused naming both, never one picked by order.
    3. Each PR is independently releasable: a one-holder deployment's answers do not move unless the changeset declares it.
  • The precedent narrowing for webhook (merged as e55347d, changeset .changeset/31699-webhook-names-per-namespace.md in git history) for the expected shape of the declaration.

What to judge

  1. Red lines 1 to 3 across every reader that names a connector: the reconcile (materialization, refusal of an entry with no package of its own, duplicate checks, the no-registry composition), the engine's resolveConnectorInstanceFor (every arm, including the plugin code-registered exception and the disabled-only arm), the connector_action dispatch, the connector pull (connector resolved from the mapping's package; provider, origin, degraded state, handler and inputSchema read from that one instance), the connection door (bare and namespace address; request.package; the recorded package of a begun connect), the OAuth2 grant handles keyed by instance, the credential listing, descriptors.
  2. A disabled holder, in both registration orders, at every door that serves or dispatches.
  3. Whether anything still keyed by bare name can mix two packages' instances (maps, caches, log keys, the materializer's retry timers, the credential-refusal map, grantHandles).
  4. The completeness and truth of the changeset's FROM → TO list and its one-holder sentence, against the code.
  5. Whether the tests pin what they claim (look for vacuous pins), and whether an important path has no pin.
  6. The stated deviation (namespace address instead of ?package=): acceptable or not, and why.

Output

Write the report to /mnt/project-files/server-seat-7/r2026-11-14/31699-narrow-review-r1.md and return it. Format:

  • Verdict: PASS, PASS-WITH-CHANGES, or FAIL.
  • Must-fix (blocks landing), should-fix, nits: each with file:line, the concrete failing scenario (inputs → wrong answer), and the fix.
  • A short Clause-② judgement: is yes (narrowing) right, and is every moved answer listed.

Do not edit the worktree, push, or post anywhere. Running tests read-only is fine (cd packages/services/service-automation && npx vitest run <file>), but do not leave files changed.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzKCovWpJoMZD6U3NT

Contract review r1 — #31699 narrowing PR (connector), commit 4a9d32b9

Reviewer: isolated agent, no part in the change. Read: every hunk of 4a9d32b9, the changeset, the PR body draft, issue comments 6871869590, 6879929803, 6816295828, 6820178514, 6882996364, and the surrounding code the hunks depend on. Five claims below were measured with probe tests run from a scratch directory against the worktree's source (nothing in the worktree was written; git status is clean at 4a9d32b9). Each finding says whether it was run or only read.

Verdict: PASS-WITH-CHANGES

Red lines 1 and 2 hold on every path I traced: I found no caller served or sent another package's instance, and no instance picked by order. Two defects block landing: one answer moves for a one-holder deployment without being declared (red line 3), and one bare-name key still mixes two packages' instances.

Must-fix

M1. The credential listing loses state and degradedReason for a one-holder instance whose entry carries a sentinel stamp (red line 3, undeclared)

  • Where: packages/services/service-automation/src/plugin.ts:5159-5163 (declaredCredentialRefs keeps the raw _packageId) and plugin.ts:5196-5200 (descriptorOf matches d.packageId === c.packageId).
  • Scenario (run): one declared provider-bound entry billing with _packageId: 'sys_metadata' (the environment's stored row) or '__runtime__' (the compatibility default), credential missing. The instance registers under the package connectorInstancePackage gives it (none, or the name's holder), so its descriptor carries no packageId, or the holder's.
    • FROM (main): listConnectorCredentialRefs() answers connectors: [{ name: 'billing', packageId: 'sys_metadata', state: 'degraded', degradedReason: … }] (the descriptor was found by name).
    • TO (this commit, measured): connectors: [{ name: 'billing', packageId: 'sys_metadata' }]. Same for '__runtime__'.
  • This is a one-holder answer that moves, and the changeset's one-holder sentence does not list it.
  • Fix: look the descriptor up by the instance's package, not the raw stamp: connectorInstancePackage(resolve, entry) (or at least declaredEntryPackage), null matching a descriptor with no packageId. Add a pin with a sys_metadata entry, and one with two packages' instances in different states (the changeset claims the per-instance listing and nothing pins it).

M2. The reload merge supersedes by bare name, so a reload of one package's artifact tears down another package's instance of the name

  • Where: plugin.ts:1329-1335 (mergeDeclaredConnectorSources): a registry entry is dropped when declaredNames.has(name), whatever its package.
  • Scenario (the merge was run; the consequences below are read from the reconcile): host package com.acme.host and installed package com.acme.beta (another namespace) each declare a provider-bound billing; both are materialized. A metadata:reloaded payload of the host's artifact carries connectors: [billing].
    • Stamped payload item: the merge returns only the host's entry. com.acme.beta/billing is no longer desired, so the reconcile dematerializes it, and reloadedConnectors persists, so every later reconcile and the connection door (readDeclaredConnectorItems) omit it until restart. Beta's flows fail "no handler", and its namespace door answers 404.
    • Unstamped payload item (the existing control at connector-materialization.test.ts:1115 shows this shape is supported): the merge returns one entry with no package of its own. Under two holders connectorInstanceRefusal refuses it, so both instances are gone after the reload.
  • It fails closed (nothing is served to the wrong caller), but it is exactly the bare-name mixing the brief asks about, and the two-holder state is opened by this PR.
  • Fix: supersede by (package, name): drop a registry entry only when the artifact re-declares its name under the same package, or under one of owners for an unstamped item; treat an unstamped artifact item as its manifest's package (owners) rather than as an entry with no package. Pin: two holders, reload of one package's artifact, the other's instance still dispatches.

Should-fix

S1. A caller inside the disabled package is still served its own instance; the changeset, docblock, docs and one test title say "no caller"

  • Where: engine.ts:5946-5959. The registry keeps the caller's own holding in the served set (objectql/src/registry.ts:9535-9537), so resolver(name, A) is resolved (own) for a disabled A, and the resolved arm returns A's instance while it is registered.
  • Scenario (run): A and B hold billing, both instances registered, A disabled: resolveConnectorInstanceFor('billing', A) is { status: 'instance', packageId: A }. Same with A the lone holder.
  • This is main's answer, and it is the package's own body, so no red line breaks. But it is the exact case note 6816295828 names ("a flow of a disabled package"), and nothing pins it: the tests at flow-package-scoped-resolution.test.ts:932 and :961 use P (a dependent of A) as "A's own caller", and the second is titled "served to no caller".
  • Not true as written: changeset item (7) "for every caller"; the docblock "a disabled package's instance is never served"; the docs line "its instance is never served".
  • Fix: either answer codeRegisteredInstance when the resolved package holder is disabled (then every statement is true), or keep the answer, say so in the three places, and pin caller = A in both registration orders.

S2. Changeset item (5) gives the wrong FROM for catalog holders beside a plugin's connector

  • Scenario (run): A and B each declare a catalog entry slack (no provider), a plugin registers slack in code. resolveConnectorInstanceFor('slack', null) is refused; for caller A it is the plugin's instance.
  • FROM (main, a boot-sealed clash): an environment-authored flow dispatched the plugin's connector (the one instance of the name). TO: refused naming both. Item (5) says FROM "not dispatched as not registered".
  • Fix: add the row to the changeset. Or decide that a name whose every candidate has no instance of its own answers the plugin's connector for a caller with no package too, since every package caller already gets it.

S3. Holders the namespace address cannot tell apart are now materialized, and no HTTP door reaches them

  • The reconcile no longer refuses two stamped entries of one name, whatever their namespaces (plugin.ts:3402-3432). The install door still refuses a same-namespace co-owner and a namespace-less package, but a cold boot only warns (BARE_NAME_COLD_BOOT_MODE 'warn').
  • Scenario (read, not run): two code-declared packages of one namespace both declare provider-bound crm with authorization_code. FROM: boot refused naming both. TO: both materialized; the bare door is 409, and /connectors/NS/crm/connection is 409 ambiguous-in-namespace (automation-address.ts:300-302). Neither can be connected over HTTP. ?package= would have reached them.
  • The docs say such holders "still may not" coexist, which is true only of the install door.
  • Fix: either keep the reconcile's refusal for holders the namespace rule does not admit (the boot answer then does not move for them), or state in the changeset and docs that they materialize and are connectable only in process (request.package).

S4. "The reconcile drops a disabled package's instance on its next run" does not cover an entry with no package of its own (read, not run)

  • connectorInstancePackage judges over every holder (includeDisabledPackages), and listItems hides only items stamped with the disabled package. An unstamped entry whose name only a disabled package D holds stays declared, is registered under D on every reconcile, and the disabled arm answers none for every caller, permanently rather than "until the next reconcile".
  • FROM: a caller with no package dispatched it (the one instance of the name). The changeset's (7) and its BREAKING paragraph describe this as transient.
  • Fix: word (7) for this case, or have the reconcile not materialize an entry whose instance package is disabled.

S5. Missing pins on paths the brief names

  • A disabled holder at the connection door under two holders: the namespace address of the disabled package (read: slot A, then 404 because A's entry is not listed), and the service's bare door (read: resolves to the enabled holder). Neither is pinned in either registration order.
  • A pull whose connector has two holders, one disabled.
  • The credential listing with two instances (see M1), and the reload with two holders (see M2).

Nits

  • plugin.ts:4271: the grant handle is keyed by the instance but reads its connection by a package re-resolved on every read. For an unstamped entry, between a second holder's install and the next reconcile, the handle of P/name reads as package null. It fails closed (read: not connected). Pass the instance's fixed packageId.
  • connector-connection-door.test.ts:1265: if (revoked) await expect(...) asserts nothing when the handler is absent. Assert the state explicitly (not connected or degraded for CRM).
  • plugin.ts:3177: the inert-descriptor audit builds live by name, so package A's catalog entry is silenced by package B's live instance of the name.
  • plugin.ts:3558 and the connect, revoke and grant-refusal log lines name the instance without its package (inPackage exists).
  • PR body: RESULTS_AFTER_REBASE, GATES_LINE and REVIEW_LINE are unfilled, and "单持有者部署只变三处" needs M1's outcome.

What I checked and found sound

  • Reconcile: each stamped entry is its own instance; an entry with no package under two holders is refused naming both and registered as neither; a duplicate within a package is refused; the no-registry composition keeps one instance per name. desired, materialized, degraded, credential-refusal attempts and grantHandles are keyed by instance; unregister is by (name, package). The retry timer is one global timer over all degraded instances, and credentialRefusals is keyed by credential ref: neither mixes instances.
  • Engine: refused is passed through; resolved reads exactly the holder's instance; the plugin exception cannot return a declarative instance (assertSameOriginOrFree keeps the two origins off one name); the no-resolver and not-found arms answer the sole instance or none.
  • connector_action: handler, degraded reason, hold, effect and inputSchema are all read by the resolved package.
  • Pull: origin, degraded state, provider, handler and inputSchema are read from the one resolved instance; refused is 409 naming both; none is connector_not_registered. The four pins are not vacuous.
  • Connection door: the recorded package wins over the addressed one; the addressed package is read exactly; grantHandles seed and forget by instance; the two-instance test proves two rows and two distinct tokens. N6 is judged and pinned.
  • Disabled holder in both orders for a dependent caller and for a caller with no package: pinned and correct.

The deviation (namespace address instead of ?package=)

Acceptable. The address already exists (child G1), it is the ADR's D5 spelling, and for the state D9 admits (two holders of different namespaces) it names exactly one holder; a second spelling would add a door to keep consistent. GET /connectors unfiltered with packageId on each descriptor is additive. Its one cost is S3: holders that share a namespace or have none cannot be addressed, so the choice should come with a decision on S3.

Clause-② judgement

yes (narrowing) is right. The FROM → TO list is not complete or fully true:

  • Missing: M1 (a one-holder answer, so the one-holder sentence is also incomplete).
  • Wrong FROM: (5) for catalog holders beside a plugin's connector (S2).
  • Overstated: (7) "for every caller" (S1), and the "next run" claim in the BREAKING paragraph (S4).
  • Items (1) to (4), (6), (8), (9) and (10) match the code as I read it.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzKCovWpJoMZD6U3NT

Contract review, delta after r1 — #31699 narrowing PR (connector), head de90aedb

Scope: git diff 4a9d32b9 de90aedb only (9 files; code changes in service-automation/src/plugin.ts, a docblock in engine.ts, tests, changeset, docs), judged against the r1 findings and for anything the delta itself breaks. Worktree not modified (git status clean at de90aedb).

Verdict: PASS

No must-fix item remains, and the delta introduces none that I found.

What was run

  • The six touched or adjacent test files on de90aedb: connector-connection-door, connector-materialization, connector-descriptor-audit, connector-pull, flow-package-scoped-resolution, connector-credential-door: 329 passed, 0 failed.
  • My r1 probes again, from a scratch directory:
    • M1: an entry stamped sys_metadata with no package holder, and one stamped __runtime__, each with a missing credential. The listing now answers state: 'degraded' and the degradedReason for both, with the public packageId still the raw stamp (main's answer).
    • M2: mergeDeclaredConnectorSources with a host item stamped com.acme.host over registry entries of com.acme.host and com.acme.beta: both entries are returned, beta's kept.
  • I did not re-run the full service-automation suite or ablations A12 to A14; those are the author's figures.

Per finding

  • M1, closed. declaredCredentialRefs carries the instance's package and descriptorOf matches on it (plugin.ts, (d.packageId ?? null) === c.instance). The instance field does not reach the public answer (the mapping builds name, packageId, state, degradedReason explicitly). Dedupe by name and instance gives main's answer where two entries of one name are one instance. Both new pins assert state, not just presence.
  • M2, closed. The merge supersedes by (package, name), and the capture stamps an unstamped item with the manifest id. I checked that this id is the one the registry files the same items under: the residual rule uses manifest.id ?? stack.id (spec/src/stack.zod.ts:9035), the same expression as the capture, so a one-holder reload still replaces its own entry through owners. The pin proves one teardown (A's) and one materialization of B.
  • S1, closed by wording and pins. Main's answer is kept; the changeset (7), the engine docblock and the docs now say "a caller outside the package", and caller = A is pinned in both orders and as the lone holder.
  • S2, closed. Changeset (5) carries the plugin FROM; the new pin covers both package callers and the caller with no package.
  • S3, closed by declaration. Changeset (2) and the docs state that a pair the install door refuses is materialized and is connectable only in process. This is a decision recorded, not a refusal; I accept it as declared.
  • S4, closed by wording. Changeset (7), the BREAKING paragraph and the docs say the unstamped entry over a lone disabled holder is not dispatched until the package is enabled.
  • S5, closed. New pins: bare door and CRM-addressed door with CRM disabled, both orders (404, no row written); a pull with one holder disabled; plus the M1 and M2 pins.
  • Nits, closed. The grant handle takes the instance's fixed package (the rewritten 3b test pins that the registered instance still reads its own row and that the reconcile then materializes neither). The revoke test asserts state unconditionally. The audit judges each entry through resolveConnectorInstanceFor for its own package; I traced the one-holder cases (catalog entry beside a plugin connector, no plugin, no registry, an environment row) and each answers as main did. Log lines carry the package.

Notes, none blocking

  • An artifact with no manifest id and unstamped items still supersedes every entry of the name, and under two holders the reconcile then refuses that item, so both instances go on that reload. The docblock says so; the changeset bullet says only "an unstamped item is its manifest's". Worth one clause in the changeset.
  • In a composition whose registry offers no package-scoped reads, a reloaded item of package H beside a registry entry of package B under one name now leaves both declared, and the reconcile refuses the second as a duplicate (main: the artifact's superseded B's). Read, not run; it needs two packages on one name with no judging registry.
  • The M1 pin for sys_metadata covers the holder case; the no-holder and __runtime__ cases pass in my probe but have no pin of their own.
  • PR body: the three placeholders (RESULTS_AFTER_REBASE, GATES_LINE, REVIEW_LINE) and the one-holder count line still need filling against this head.

Clause-② judgement

yes (narrowing) stands. With the reworded (2), (5) and (7), the BREAKING paragraph and the new reload and listing bullet, every moved answer I found in r1 is now either fixed back to main's answer (M1) or declared with its FROM and TO.


delta-2 — head 510a840 (git diff de90aedb 510a8400)

Verdict: PASS

No must-fix item. Scope: this delta only (changeset, plugin.ts, one test).

  • Note 2, fixed. mergeDeclaredConnectorSources supersedes by (package, name) only with byPackage: true; otherwise by name, as main did. readDeclaredConnectorItems computes byPackage with packageScopedRegistryOf(ql.registry) !== undefined, the same predicate the reconcile uses for judgedByPackage, so the merge and the reconcile agree on when two instances of a name may live. The function's only caller is that one site, and the default is the main behaviour (false).
  • Note 1, declared. The changeset says an artifact with no manifest id, or a composition with no package-scoped registry, still supersedes by name.
  • Note 3, pinned. The M1 environment-row pin runs for sys_metadata and __runtime__.

Run by me on 510a840: connector-materialization.test.ts and connector-descriptor-audit.test.ts, 124 passed, 0 failed. Not re-run: the full suite (the author reports 3941) and tsc. The by-name arm for a registry with no package-scoped reads has no pin of its own; it is main's behaviour and not blocking. Worktree untouched.

Still open from the earlier record, outside this delta: the PR body placeholders and its one-holder count line.


delta-3 — head e467615b, rebased on main 4ace1fa (conflict with #33193, b462444)

Verdict: PASS

No must-fix item. Scope: the conflict resolution and the new clause in changeset row (3).

The resolution is right. connector is in PACKAGE_SLOTTED_METADATA_TYPES (packages/spec/src/api/protocol.zod.ts:2337), and PACKAGE_SLOTTED_STORE_TYPES is built from it (packages/metadata/src/package-slotted-store.ts:147), so there is one list and the metadata hunk is docblock only. The branch's diff to packages/spec is that one line plus the two interface members already reviewed.

The clause is right, read against historySlotOf:

  • ?package= on a connector: FROM refusePackageSelectorOutsideSlottedTypes throwing 400 VALIDATION_ERROR (connector not in the list on main), TO the slot of the row a read naming the package serves, or the package's own slot when it ships the name.
  • A bare connector name two packages hold: FROM historySlotOf returning undefined for a type outside the list (the door served the name's whole log), TO 409. Two paths reach it, and both hold for connector now: two row slots (ambiguous-in-environment), and refuseWhereBareReadRefuses, which fires because this PR also puts connector in PACKAGE_SCOPED_READ_TYPES, so two holders in code are refused even with one stored row.
  • A one-holder name, bare: one slot, sole: true, the whole log and any version restorable, as on main.

Nothing else in #33193 needs connector handling that I found. The type list appears once in that commit; every other site reads PACKAGE_SLOTTED_STORE_TYPES or isPackageSlottedMetaType, so the REST routes, the repository's version attribution, the Studio history page and the metadata client pick connector up from the list. No generated spec artefact or reference doc embeds the list's values (searched packages/spec/*/api.json and content/docs/references/api/protocol.mdx).

One wording note, not blocking. The bare door also refuses a name whose rows sit in a package's slot and in the package-less slot (one package holder, two row slots). That is the rule every slotted type already has, and it needs a row saved under the package beside an unbound environment row, so existing one-row connector data is unaffected. The clause could add "or a package's row beside the package-less row" for completeness.

Run by me on e467615b: packages/metadata-protocol/src/protocol-history-slots.test.ts, 15 passed, 0 failed. Not run by me: the other suites and spec check:generated, which the author has in progress; this PASS assumes they come back green. Worktree untouched.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

维护者速读 · PR #33431 @ 1530146d · 2026-11-14T21:47Z

席位核对

  • 形态: draft → main。正文写 Fixes #31699,合并后卡关闭;正文其他地方没有挨着别的 open 卡号的闭合关键词。
    • 申报 Clause-②: yes (narrowing)(十条 FROM → TO,(3)-(8) 标 BREAKING)、Metadata-protocol: added,ADR-0087 标记 not-required (no-migration-prescription);changeset 七个包,metadata-protocol 为 patch,其余 minor。
    • 文件面 = 接手认领 6857670747 与四次修订(第四次 6882996364 列出本 PR 的文件),恢复认领 6878990173。37 个文件中 36 个在修订面内;packages/spec/src/api/protocol.zod.ts 一行(connector 加入 PACKAGE_SLOTTED_METADATA_TYPES)是变基到 fix(metadata-protocol)!: history and rollback doors refuse a bare two-holder name and address one holder by ?package= #33193 之上的必然结果,delta-3 专门审过,本席接受。
  • 前提实测: 卡线程的 dev 报告 6869754560、6878937714、6879908528 与交接 6871869590 / 增补 6879929803 列出收窄清单;本 PR 逐项落实,偏差(用已有的命名空间地址代替 ?package=)在修订 6882996364 中事先说明。新钉由卡线程消融 A1-A14 全部变红再恢复;本席不把线程计数当证据,以 CI 为准。
  • 门禁 check-runs @ 1530146d: 62 success、14 skipped、0 失败、0 进行中。
  • 契约复审: CONTRACT_REVIEW_TIER,隔离审读子代理,简报 6884423744 贴在旁边,本席核对后采纳。
    • r1 6884429121 在 4a9d32b9 上 PASS-WITH-CHANGES:必修 M1(单持有者下凭据列表丢失 state / degradedReason,未申报)、M2(重载合并按裸名覆盖,会拆掉另一个包的实例);应修 S1-S5 与细节。
    • delta 6884435741(三段):de90aedb PASS(M1、M2、S1-S5 关闭);510a8400 PASS(无包作用域注册表时按名覆盖,与 main 一致并已申报);e467615b PASS(变基到 fix(metadata-protocol)!: history and rollback doors refuse a bare two-holder name and address one holder by ?package= #33193,connector 进入可 ?package= 的历史 / 回滚门,裸名两持有者 409)。
    • e467615b → 1530146d 只改 changeset 两行(第 (3) 行补上 delta-3 的措辞建议、ADR-0087 理由补一句),没有代码或测试变化,本席已读。
  • 维护者裁定: 卡面只申报了同命名空间共有者的收窄;三处单持有者变化超出申报,按卡的验收条款呈裁(6884489751),维护者选 A「按申报接受,CI 绿后落地」,记录 6884569185。
  • 接受的偏差:
    • 连接门用已有的命名空间地址 /automation/connectors/:ns/:name/connection 代替交接写的 ?package=;GET /connectors 不加过滤,每个描述符带 packageId(修订 6882996364)。
    • S3:安装门仍拒绝的一对(同命名空间或无命名空间的包)若由代码启动,两者都物化,只能在进程内连接(命名空间门对二者答 409),已申报。
    • delta 记录中未阻塞的细节:无包作用域注册表的按名分支没有单独的钉(与 main 行为相同)。
  • 范围外去向: 无新卡。权限的游离行问题属于 metadata-protocol: the boot hydration binds a stored webhook row of a name two packages hold by registration or load order; key its artifact lookup by (package, name) (ADR-0150 D3/D4) #32692(维护者待答,os-dev-report 6868371930);metadata-protocol: the boot hydration binds a stored webhook row of a name two packages hold by registration or load order; key its artifact lookup by (package, name) (ADR-0150 D3/D4) #32692 第二半是 metadata-protocol: after a restart, two stored rows of one name that each name a different holder leave only the last one loaded, for every package-slotted type (tool, webhook, …) #33127(p2,已派发)。
  • 落地条款: 维护者常设落地令 「按 pm-dispatch 审过的pr你可以自主落地」 (2026-10-30T14:20Z),并按 「车道座位应该使用pm-dispatch 技能审核和落地pr」 (2026-11-14T14:32Z) 落地;本 PR 的收窄另有维护者裁定 A (2026-11-14T21:35Z)。
    • 非受管面:check-governed-merges --pr 33431 判定 NOT governed。1758 行(+1349 / -409)≤ 3000。
    • K = 12(PRBASE ba35dfd7,取自 Plan Test Core shards 的 Affected-set diff base),< 15。其后 main 有三个文件与本 PR 重叠,都是测试:stored-references.integration.test.ts(test(cli): stored-references binds name fixed row ids, and a run with no JSON says why #33426)、database-loader.test.ts 与 metadata-manager-match-endpoint.test.ts(3825f576,metadata unregisterPackage);git merge-tree 无文本冲突,语义交给合并队列的全量套件。
    • 没有新的语种包。

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 14, 2026 21:48
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 14, 2026
Any commits made after this event will not be merged.
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 43644860217 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Dogfood Regression Gate (3/3) — 失败步骤: Boot example apps and exercise real user flows

    @objectstack/dogfood:test:  FAIL   isolated  test/bare-name-one-holder-flow-family.dogfood.test.ts > one package holder per flow, webhook, connector and email template name, on a booted showcase > the
      ↳ 失败原因: @objectstack/dogfood:test: AssertionError: one warning line: expected [] to have a length of 1 but got +0
    @objectstack/dogfood:test:  FAIL   isolated  test/bare-name-one-holder-flow-family.dogfood.test.ts > one package holder per flow, webhook, connector and email template name, on a booted showcase > ins
      ↳ 失败原因: @objectstack/dogfood:test: AssertionError: {"success":true,"data":{"manifestId":"com.qa.bare.delta","version":"1.0.0","versionId":"1.0.0","installedAt":"2026-11-14T21:57:47.644Z","hotLoaded":true,"upg
    @objectstack/dogfood:test:  FAIL   isolated  test/bare-name-one-holder-flow-family.dogfood.test.ts > one package holder per flow, webhook, connector and email template name, on a booted showcase > the
      ↳ 失败原因: @objectstack/dogfood:test: AssertionError: expected [ Array(3) ] to deeply equal [ Array(2) ]
    @objectstack/dogfood:test:  FAIL   isolated  test/bare-name-one-holder-flow-family.dogfood.test.ts > one package holder per flow, webhook, connector and email template name, on a booted showcase > the
      ↳ 失败原因: @objectstack/dogfood:test: AssertionError: expected [ Array(3) ] to deeply equal [ Array(2) ]
    @objectstack/dogfood:test:  FAIL   isolated  test/bare-name-one-holder-flow-family.dogfood.test.ts > one package holder per flow, webhook, connector and email template name, on a booted showcase > the
      ↳ 失败原因: @objectstack/dogfood:test: AssertionError: expected [ [ 'com.qa.bare.alpha', true ], …(2) ] to deeply equal [ [ 'com.qa.bare.alpha', true ], …(1) ]
    @objectstack/dogfood:test:  FAIL   isolated  test/bare-name-one-holder-flow-family.dogfood.test.ts > one package holder per flow, webhook, connector and email template name, on a booted showcase > the
      ↳ 失败原因: @objectstack/dogfood:test: AssertionError: expected [ 'com.qa.bare.alpha', …(3) ] to deeply equal [ 'com.qa.bare.alpha', …(2) ]
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 14 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

… namespace rule

The cold boot of two namespaces holding the four names logs no bare-name warning; a third namespace's package declaring the held connector name installs; the bare /meta/connector/NAME refuses the name naming every holder, each namespace address reads its own holder's declaration, and a co-owner of alpha's namespace is still refused by the bare-name rule.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzKCovWpJoMZD6U3NT
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 35a988b6bba31b9973c95fb5c2f0798fc213f3ec
Local-runs: none
Scope: packages/qa/dogfood/test/bare-name-one-holder-flow-family.dogfood.test.ts

The seat's own read of the part of this head that no record covers, 1530146d..35a988b6, which is two commits. ca5e3523 merges main (b392ff7b), and its combined diff is empty, so it carries no hand edit. 35a988b6 changes one dogfood test file (+66 / −49) after the merge queue's Dogfood Regression Gate (3/3) failed on this PR's queue commit 063d2e0d (run 43645197239, job 132601418057). Every other part of this head is judged by r1 6884429121 and the delta records in 6884435741, under brief 6884423744, and by the seat's ACCEPT 6884710089.

① Derived judgments

  • The cold-boot pin moves from one bare-name warning naming the connector to no warning line. That is changeset row (2): two namespaces' connectors are no clash. The flow-slot and webhook-refusal checks stay. Right.
  • The delta install moves from 422 to admitted. That is changeset row (1). delta no longer declares the held webhook, so the webhook block keeps its own two holders. The third namespace's webhook is still pinned through theta. Right.
  • A new connector block in the webhook block's shape. The bare /meta/connector/NAME answers 409 RESOURCE_CONFLICT naming all three holders (row (3)). Each holder's /meta/connector/NS/NAME reads its own declaration. A CONTROL shows that a co-owner of alpha's namespace is still refused 422 PLUGIN_REGISTER_FAILED, naming alpha only, with nothing installed. That matches the card's declared narrowing (co-owners of one namespace). Right.
  • Removing the now-unused levelOf and SHOWCASE_PKG is lint hygiene.
  • The old pin that the boot warning never names the built-in templates' package has nothing to check now that no warning line exists. What remains asserts that the built-in templates are registered under a package. Acceptable.

② Semver level

Unchanged. A test-only delta, with nothing published.

③ Boundary flags

None new. One finding outside this PR is filed as #33507: bareNameReason still gives the one-name-per-deployment reason for the four namespace-scoped types. It predates this PR.

④ Design judgment

No design moves. The card thread reports a full dogfood run at this head: 467 test files passed, 3 skipped; 3953 tests passed, 42 skipped. The check-runs on this head, which are the evidence: 58 success, 10 skipped, 0 failed, 0 in progress.

Implemented-by: claude/project-thread-y6vjs1
Reviewed-by: session_01L9qibzuZJBQY4wUQFJbkfx

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

Re-land · 2026-11-14T23:22Z · PR #33431 @ 35a988b6

  • Why it left the queue: the queue's Dogfood Regression Gate (3/3) failed on this PR's queue commit 063d2e0d (run 43645197239). bare-name-one-holder-flow-family.dogfood.test.ts still pinned the connector's old one-holder rule. That gate does not run on the PR side.
  • What moved since ACCEPT 6884710089 (1530146d): ca5e3523 merges main with an empty combined diff, and 35a988b6 moves that one dogfood file to the declared narrowing (test only). Seat record 6885822092, PASS. The card thread ran the full dogfood suite at this head: 467 files passed / 3 skipped; 3953 tests passed / 42 skipped.
  • Gates at 35a988b6: 58 success, 10 skipped, 0 failed, 0 in progress.
  • K = 1 (PRBASE f068cde8). No new locale pack and no shared file since then, and git merge-tree is clean.
  • The maintainer's ruling A (6884569185) is unchanged. The finding outside this PR is objectql: the bare-name refusal still explains itself with the old one-name-per-deployment rule for the four types that now hold one name per namespace #33507.

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 14, 2026
Any commits made after this event will not be merged.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

2 participants